/* A simple server in the internet domain using TCP
   The port number is passed as an argument */
#include <stdio.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <boost/foreach.hpp>
#include <boost/tokenizer.hpp>
#include <vector>

#include "Alfred.h"
//#include "Game.h"

#define BUFFER_SIZE 1024

using namespace std;
//Global Enums
//enum Difficulty { EASY, MEDIUM, HARD };
enum ACK		{ WELCOME, OK, move, ILLEGAL, comment, whitewin, blackwin};
enum MODE		{ HUMAN_AI, AI_AI };

//each game will need to have own values
Difficulty	difficulty	=	EASY; 
bool		display		=	false;
MODE		game_play	=	HUMAN_AI;
bool		turn		=	false; // true is server turn.
int sockfd, newsockfd;


Game my_game;
Alfred al(EASY,WHITE,&my_game);




void error(const char *msg)
{
    perror(msg);
    exit(1);
}

void shutdown() {
	close(newsockfd);
    close(sockfd);
}

void toggle_display() {
	display = !display;
}

void set_difficulty(string val){
	if(val == "EASY"){
		difficulty = EASY;
	} else if(val == "MEDIUM"){
		difficulty = MEDIUM;
	} else if(val == "HARD"){
		difficulty = HARD;
	} else {
		//this should never happen
		cout<<"ERROR: Set diffiulty, given"<<val<<endl;
	}
}

bool is_move(string move) {
	if(move.length() != 2) {
		return false;
	}
	char x_pos = move[0];
	char y_pos = move[1];

	if( x_pos == '0' || y_pos == '0' ){
		return false;
	}

	if( isxdigit(x_pos) && isxdigit(y_pos) ) {
		return true;
	} else {
		return false;
	}

}

void send_ack(ACK a, int sockfd, Game& game) {
	string message = "";

	switch (a)
	{
		case WELCOME:
			message = "WELCOME";
			break;
		case OK:
			message = "OK";
			break;
		case move:
			message = "I dont know what goes here";
			break;
		case comment:
			message = "I dont know what goes here";
			break;
		case whitewin:
			message = "***WHITE WINS***";
			break;
		case blackwin:
			message = "***BLACK WINS***";
			break;
		default:
			message = "Illegal";
	}

	message+="\n";

	
	if(display) {
		//right now, display() returns void, need to change it to return a string
		string board_string = game.display();
		
		//Find a workaround to c_str() or a way to handle newline chars
		//write(sockfd, board_string.c_str(),board_string.length());

		message += board_string;
	}

	write(sockfd,message.c_str(),message.length());
};


int to_hex(char c) {
	if( 48 < c && c < 58 ) {
		c = c - 48; //for 1-9
	} else if(c > 16) {
		c = c - 55; //for capital letters
	} else if(c > 16) {
		c = c - 87; //for lowercase letters
	}

	return c;
}

int make_move(string move, int sockfd, Game& game) {
	int x_pos = to_hex(move[0]);
	int y_pos =  to_hex(move[1]);

	//cout<<"x_pos = "<<x_pos<<endl;
	//cout<<"y_post = "<<y_pos<<endl;

	
	game.move(x_pos, y_pos, BLACK);
	if(game.check_state() == 1)
	{
		send_ack(blackwin, sockfd, game);
		cout<<"Make move : 1\n";
		return 1;
	}
	//Al handles the server move
	al.move();
	if(game.check_state() == 1)
	{
		send_ack(whitewin, sockfd, game);
		cout<<"Make move : 1\n";
		return 1;
	}
	cout<<"Make move : 0\n";
	return 0;
}

bool validate_move(string move, Game& game) {
	int x_pos = to_hex(move[0]);
	int y_pos =  to_hex(move[1]);

	bool return_val = game.validate_move(x_pos, y_pos);
	cout<<"Validate Move : " << return_val << endl;
	return return_val;
}



bool parse(vector<string> message, int sockfd, Game& game) {
	for(size_t i=0; i< message.size(); i++) {
		
		if(message[i] == "EXIT") {
			///shutdown should return a value
			send_ack(OK, sockfd, game);
			shutdown();			
		} else if(message[i] == "DISPLAY") {
			toggle_display();
			send_ack(OK, sockfd, game);
		} else if(message[i] == "EASY" || message[i] == "MEDIUM" || message[i] == "HARD"){
			set_difficulty(message[i]);
			send_ack(OK, sockfd, game);
		} else if(message[i] == "UNDO") {
			game.undo();
			send_ack(OK, sockfd, game);
		} else if(message[i] == "HUMAN-AI") {
			//TODO: human vs AI mode
			game_play = HUMAN_AI;
			send_ack(OK, sockfd, game);
		} else if(message[i] == "AI-AI") {
			game_play = AI_AI;
			send_ack(OK, sockfd, game);
		} else if(is_move(message[i])){
			if(!validate_move(message[i], game)) {
				cout<<"ILLEGAL 1\n";
				send_ack(ILLEGAL, sockfd, game);
			} else {
				//if the game is not over
				if(	make_move(message[i], sockfd, game) == 0) {
					send_ack(OK, sockfd, game);	
				} else {
					shutdown(); 
				}
			}			
		} else {
			cout<<"ILLEGAL 2\n";
			send_ack(ILLEGAL, sockfd, game);
		}
	}
	return true;
}


vector<string> tokenize(char* buf) {
	vector<string> return_vec;
	string word = "";

	for(size_t i=0; i<strlen(buf); i++){
		if(isspace(buf[i])) {
			if(word.length() > 0) {
				return_vec.push_back(word);
				//cout<<"Word	:	"<<word<<endl;
				word = "";
			}
		} else {
			word = word+buf[i];
		}

	}
	if(word != "") {
		return_vec.push_back(word);
		//cout<<"Word	:	"<<word<<endl;
	}

	return return_vec;
}

int main(int argc, char *argv[])
{
     int portno;
     socklen_t clilen;
     char buffer[BUFFER_SIZE];
     struct sockaddr_in serv_addr, cli_addr;
     int n;

     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided\n");
         exit(1);
     }

	 //Get the socket
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd < 0) 
        error("ERROR opening socket");

     bzero((char *) &serv_addr, sizeof(serv_addr));
     portno = atoi(argv[1]);

	 //Set ther server address parameters
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(portno);

	 //Bind to the socket
     if (bind(sockfd, (struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
              error("ERROR on binding");

	 //Listen for the client to connect
     listen(sockfd,5);

	 //Establish the connection
     clilen = sizeof(cli_addr);
     newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr,&clilen);
	 //create game objects
	 //would fork here
     if (newsockfd < 0) {
          error("ERROR on accept");
		  return 0;
	 }


	 send_ack(WELCOME, newsockfd, my_game);
	 
	 char close_str[6] = "Close";

	while(true){
		 bzero(buffer,256);
		 n = read(newsockfd,buffer,255);
		 if (n < 0)
			 error("ERROR reading from socket");

		if(strncmp(buffer,close_str,5) == 0) {
			printf("Close message received. Closing connection\n");
			break;
		}

		vector<string> read_message = tokenize(buffer);
		printf("Here is the message: %s\n",buffer);
		//should be taken care of by parse n = write(newsockfd,"I got your message",18);
		parse(read_message, newsockfd ,my_game);

		

		 if (n < 0)
			 error("ERROR writing to socket");
	}


     close(newsockfd);
     close(sockfd);
     return 0; 
}
